#ifndef _UTILS_CASTER_H_
#define _UTILS_CASTER_H_

#include <arduino/Arduino.h>

#include <utils/type_traits.h>


namespace utils
{

namespace detail
{

template <typename _T, typename _OK = void>
struct convert {};

template <typename _T>
struct convert<
    _T,
    typename utils::enable_if<utils::is_integral<_T>::value>::type>
{
  typename utils::remove_reference<
  typename utils::remove_qualifier<_T>::type>::type
  operator ()(String const& str)
  {
    typename utils::remove_reference<
        typename utils::remove_qualifier<_T>::type>::type
        value(0), power(1);

    for (int i = str.length() - 1; i > -1; --i)
    {
      value += power * _T(str[i] - '0');
      power *= 10;
    }

    return value;
  }
};

template <typename _T>
struct convert<
    _T,
    typename utils::enable_if<utils::is_floating_point<_T>::value>::type>
{
  typename utils::remove_reference<
  typename utils::remove_qualifier<_T>::type>::type
  operator ()(String const& str)
  {
    typename utils::remove_reference<
        typename utils::remove_qualifier<_T>::type>::type
        value(0);
    long power = 1;
    long comma_pow = 1;

    for (int i = str.length() - 1; i > -1; --i)
    {
      if (str[i] != '.')
      {
        value += power * _T(str[i] - '0');
        power *= 10;
      }
      else comma_pow = power;
    }

    return value /= comma_pow;
  }
};

template <typename _T>
struct convert<
    _T,
    typename utils::enable_if<
    utils::is_same<char*,
    typename utils::remove_qualifier<_T>::type>::value>::type>
{
  typename utils::remove_reference<
  typename utils::remove_qualifier<_T>::type>::type
  operator ()(String const& str)
  {
    return (_T) str.c_str();
  }
};

} // detail

template <typename _T>
typename utils::remove_reference<
typename utils::remove_qualifier<_T>::type>::type
convert(String const& str)
{ return reinterpret_cast<detail::convert<_T>*>(0x0)->operator ()(str); }

} // utils

#endif // _UTILS_CASTER_H_
